#ifndef PW_PBL_THROW_HPP
#define PW_PBL_THROW_HPP

#include <cassert>
#include <cstdlib>
#include <utility>

namespace pw_pbl
{
	/// 在常量计算时编译失败，非常量计算时异常中止
	template <typename T>
	[[noreturn]] inline void Throw(T &&obj)
	{
#if __cpp_exceptions
		throw std::forward<T>(obj);
#else
		(void)obj, std::abort();
#endif
	}

	/// 在常量计算时编译失败，非常量计算时异常中止
	[[noreturn, gnu::always_inline]] inline void Throw() { Throw("undescribed error"); }

	/// 常量求值时报错，其次系统支持异常时抛出异常，否则返回指定的值
	/// @param obj 报错或抛出异常时的提示信息
	/// @param r 无法报错和抛出异常时返回的值
	template <typename T, typename R>
	[[deprecated("not consistent")]] constexpr R ThrowOrDefault(T &&obj, R &&r)
	{
		if (std::is_constant_evaluated())
			Throw(std::forward<T>(obj));
#if __cpp_exceptions
		else
			throw std::forward<T>(obj);
#endif
		return std::forward<R>(r);
	}

	[[deprecated("use pw_pbl_assert instead")]]
	/// 标准库assert的封装，在常量计算时如果条件不成立则直接导致编译失败而不是运行时再发生异常
	constexpr void Assert(bool value, const char *msg = nullptr)
	{
		if (std::is_constant_evaluated())
		{
			if (!value)
				Throw(msg);
		}
		else
			(void)msg, assert(value);
	}
} // namespace pw_pbl

/// 增强版assert，如果是consteval时在编译时检查，否则回退到assert功能
#define pw_pbl_assert(exp, ...)           \
	if consteval                          \
	{                                     \
		if (!(exp))                       \
			::pw_pbl::Throw(__VA_ARGS__); \
	}                                     \
	else                                  \
	{                                     \
		assert(exp);                      \
	}

#endif // PW_PBL_THROW_HPP
